#ifndef FUNCFILLER_H
#define FUNCFILLER_H

#include "FiniteDiff/RectDomain.h"
#include "Core/Tensor.h"

template <int Dim>
class FuncFiller
{
public:
  ///
  // Always initialize a FuncFiller with a p-grid.
  FuncFiller(const RectDomain<Dim> &_rd) : rd(_rd) { }

public:
  template <class TFunc>
  void fillPointValues(Tensor<Real,Dim> &data, int staggered, const TFunc &expr, bool fillGhosts = true) const;

  template <class TFunc>
  void fillBdryValues(Tensor<Real,Dim-1> &data, int staggered, int D, int side, const TFunc &expr, bool fillGhosts = true) const;

  template <class TFunc>
  void fillBdryValues(Tensor<Real,Dim-1> *data, int staggered, const TFunc &expr, bool fillGhosts = true) const;

protected:
  const RectDomain<Dim> rd;
};

//============================================================

template <int Dim>
template <class TFunc>
inline
void FuncFiller<Dim>::fillPointValues(Tensor<Real, Dim> &data, int staggered, const TFunc &expr, bool fillGhosts) const
{
  auto srd = rd.stagger(staggered);
  auto delta = srd.getDelta();
  auto dx = rd.spacing();
  Box<Dim> bx = srd;
  if(fillGhosts)
    bx = bx.inflate(rd.getNumGhost());
  assert(data.box().contain(bx));
  ddfor(bx, [&](Vec<int, Dim> idx) {
    data(idx) = expr((idx + delta) * dx);
  });
}

template <int Dim>
template <class TFunc>
inline
void FuncFiller<Dim>::fillBdryValues(Tensor<Real, Dim-1> &data,
                                     int staggered,
                                     int D,
                                     int side,
                                     const TFunc &expr,
                                     bool fillGhosts) const
{
  auto srd = rd.stagger(staggered);
  auto delta = srd.getDelta();
  auto dx = rd.spacing();
  Box<Dim-1> bx = reduce(srd, D);
  if(fillGhosts)
    bx = bx.inflate(rd.getNumGhost());
  assert(data.box().contain(bx));
  Real a = (side < 0) ? (rd.lo()[D]) : (rd.hi()[D] + 1);
  ddfor(bx, [&](Vec<int, Dim-1> idx) {
    Vec<Real, Dim> loc = (enlarge(idx, 0, D) + delta) * dx;
    loc[D] = a * dx[D];
    data(idx) = expr(loc);
  });
}

template <int Dim>
template <class TFunc>
inline
void FuncFiller<Dim>::fillBdryValues(Tensor<Real, Dim-1> *data, int staggered, const TFunc &expr, bool fillGhosts) const
{
  int m = 0;
  for(int d = 0; d < Dim; ++d)
    for(int side : {-1, 1})
      fillBdryValues(data[m++], staggered, d, side, expr, fillGhosts);
}

#endif //FUNCFILLER_H
